/**
 * state = 'fullfilled' 执行resolve
 * state = 'rejected' 执行reject
 * 
 * state = 'fullfilled'
    * 如果callback 不是函数 --> resolve(this.value)
    * const x = callback(this.value)
    * 如果 x 不是promise对象 --> resolve(x) or reject(x) 和当前状态保持一致
    * 如果 x 是promise对象 --> x.then(resolve, reject)
 **/

type Executor = (resolve: Function, reject: Function) => any
type ThenCB = (res: any) => any
type State = 'pending' | 'success' | 'fail'
type Item = {
  state: State,
  thenCB: ThenCB,
  nextPromiseResolve: Function,
  nextPromiseReject: Function
}

export default class MyPromise {

  _state: State
  _value: any
  list: Item[]

  constructor(executor: Executor) {
    this._state = 'pending'
    this._value = undefined
    this.list = []

    try {
      executor(this._resolve.bind(this), this._reject.bind(this))
    } catch (error) {
      this._reject(error)
      console.error(error)
    }
  }

  static resolve(value?) {
    // 状态吸收
    // const p6 = MyPromise.reject('err msg p6')
    // const p7 = MyPromise.resolve(p6)
    // p7.catch(err => console.log(err))
    if (value instanceof MyPromise) {
      return value
    }

    let resolve, reject
    const p = new MyPromise((reso, reje) => {
      resolve = reso
      reject = reje
    })

    if (likePromise(value)) {
      value.then(resolve, reject)
    }
    else {
      resolve(value)
    }

    return p
  }

  static reject(reason?) {
    return new MyPromise((_, reject) => reject(reason))
  }

  static all(promises) {
    return new MyPromise((resolve, reject) => {
      const values = []
      let count = 0

      const add = (i, v) => {
        values[i] = v
        count++
        if (count === promises.length) resolve(values)
      }

      promises.forEach((p, i) => {
        MyPromise.resolve(p).then(
          res => add(i, res),
          rej => reject(rej)
        )
        // 其实可以写成下面这样, 但p就必须是promise对象,不然会出错
        // p.then(
        //   res => add(i, res),
        //   rej => reject(rej)
        // )
      })
    })
  }

  static race(promises: MyPromise[]) {
    return new MyPromise((resolve, reject) => {
      promises.forEach(p => {
        p.then(resolve as ThenCB, reject as ThenCB)
      })
    })
  }

  finally(callback: Function) {
    return this.then(
      res => {
        callback()
        return res 
      },
      err => {
        callback()
        throw err
      }
    )
  }

  catch(onRejected) {
    return this.then(null, onRejected)
  }

  then(onResolve?: ThenCB, onReject?: ThenCB) {
    return new MyPromise((resolve, reject) => {
      this.addItem('success', onResolve, resolve, reject)
      this.addItem('fail', onReject, resolve, reject)
      this.runItems() // Promis.resolve().then(() => alert(1))
    })
  }

  runItems() {
    if (this._state === 'pending') {
      return 
    }
    // then的回调函数执行完一定要删除, 防止反复执行
    // const p = Promise.resolve()
    // p.then(() => {console.log(1)})
    // p.then(() => {console.log(2)})
    while (this.list.length) {
      const item = this.list.shift()
      this.runOneItem(item)
    }
  }

  runOneItem(item: Item) {
    const { state, thenCB, nextPromiseReject, nextPromiseResolve } = item;

    if (state !== this._state) {
      return
    }

    pushToTask(() => {
      // 透传: 状态一致, value一致
      if (!thenCB) {
        state === 'success' ?
          nextPromiseResolve(this._value) :
          nextPromiseReject(this._value)
        return
      }
      
      try {
        const x = thenCB(this._value)
        // 状态吸收
        // const p1 = Promise.reject(1)
        // const p2 = Promise.resolve(2).then(() => p1)
        likePromise(x) === true
          ? x.then(nextPromiseResolve, nextPromiseReject)
          : nextPromiseResolve(x)
      } catch (e) {
        nextPromiseReject(e)
        console.error(e)
      }
    })
  }

  addItem(state: State, thenCB: ThenCB, nextPromiseResolve: Function, nextPromiseReject: Function) {
    this.list.push({
      state,
      thenCB,
      nextPromiseResolve,
      nextPromiseReject
    })
  }

  _resolve(value?: any) {
    this.setState('success', value)
  }

  _reject(value?: any) {
    this.setState('fail', value)
  }

  setState(state: State, value: any) {
    if (this._state !== 'pending') {
      return // 状态更改后就不能变了
    }

    // 状态吸收
    // const p1 = MyPromise.reject('err msg ...')
    // const p2 = new MyPromise(res => res(p1))
    // p2.catch(err => console.log(err))
    if (likePromise(value)) {
      value.then(this._resolve.bind(this), this._reject.bind(this))
      return
    }

    this._state = state
    this._value = value
    this.runItems()
  }
}

function isNodeEnv() {
  if (globalThis.process && globalThis.process.nextTick) {
    return true
  }
  return false
}

function pushToMicroTask(cb: any) {
  const ob = new MutationObserver(cb)
  const div = document.createElement('div')
  ob.observe(div, {childList: true}) // 监听div, 发生变化就执行cb
  div.innerHTML = '1'
}

export function pushToTask(callback) {
  // node 环境用 nextTick
  if (isNodeEnv()) {
    globalThis.process.nextTick(callback)
    return
  }
  // 浏览器环境且支持 MutationObserver
  if (MutationObserver) {
    pushToMicroTask(callback)
    return
  }

  setTimeout(callback)
}


function isFn(v: any) {
  return typeof v  === 'function'
}

function isObj(v:any) {
  return typeof v === 'object' && v !== null
}

function likePromise(p: any) {
  if (
    (isObj(p) || isFn(p)) && p.then && isFn(p.then)
  ) {
    return true
  }
  return false
}